package com.example.demo.concurrent.BlockingQueueTest;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sfx
 * Date: 2023-03-16
 * Time: 14:40
 */
public class MyBlockingQueue<E> {
    private final E[] queue;
    private int capacity;
    private int size;
    public MyBlockingQueue(int capacity) {
        this.capacity = capacity;
        this.queue = (E[]) new Object[capacity];
    }
    private ReentrantLock lock = new ReentrantLock();

    private Condition fullCondition = lock.newCondition();
    private Condition emptyCondition = lock.newCondition();

    private int head;
    private int tail;

    public void offer(E e) throws InterruptedException {
        lock.lockInterruptibly();
        try{
            while(isFull()) {
                fullCondition.await();
            }
            queue[tail] = e;
            if(++tail==capacity) {
                tail = 0;
            }
            size++;
            emptyCondition.signal();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 带有超时时间的offer方法
     * @param e 加入队列的元素
     * @param timeout 超时时间
     * @return 是否成功加入到队列中
     */
    public boolean offer(E e, long timeout) throws InterruptedException {
        long nanos = TimeUnit.MILLISECONDS.toNanos(timeout);
        lock.lockInterruptibly();
        try{
            while(isFull()) {
                if(nanos<=0) {
                    return false;
                }
                //剩余的时间
                nanos = fullCondition.awaitNanos(nanos);
            }
            queue[tail] = e;
            if(++tail==capacity) {
                tail = 0;
            }
            this.size++;
            emptyCondition.signal();
        }finally {
            lock.unlock();
        }
        return true;
    }

    public E pool() throws InterruptedException {
        lock.lockInterruptibly();
        try{
            while(isEmpty()) {
                emptyCondition.await();
            }
            E e = queue[head];
            queue[head] = null;
            if(++head ==this.capacity) {
                head = 0;
            }
            this.size--;
            fullCondition.signal();
            return e;
        }finally {
            lock.unlock();
        }
    }

    private boolean isEmpty() {
        return this.size == 0;
    }

    private boolean isFull() {
        return this.size == this.capacity;
    }

    @Override
    public String toString() {
        return "MyBlockingQueue{" +
                "queue=" + Arrays.toString(queue);
    }
}